package datacache

import (
	"context"
	"cow_golang/shared_mod/datacache/iface"
	"encoding/json"
	"fmt"
	"time"

	"github.com/go-redis/redis/v8"
)

type RedisCache struct {
	client     *redis.Client
	defaultTTL time.Duration
	keyPrefix  string
	stats      iface.CacheStats
}

func NewRedisCache(config iface.Config) (iface.IDataCache, error) {
	client := redis.NewClient(&redis.Options{
		Addr:     config.RedisConfig.Addr,
		Password: config.RedisConfig.Password,
		DB:       config.RedisConfig.DB,
	})

	// 测试连接
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer cancel()
	_, err := client.Ping(ctx).Result()
	if err != nil {
		return nil, fmt.Errorf("failed to connect to redis: %v", err)
	}

	return &RedisCache{
		client:     client,
		defaultTTL: config.DefaultExpiry,
		keyPrefix:  "cache:",
	}, nil
}

func (r *RedisCache) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) error {
	if ttl == 0 {
		ttl = r.defaultTTL
	}

	jsonValue, err := json.Marshal(value)
	if err != nil {
		return err
	}

	fullKey := r.keyPrefix + key
	return r.client.Set(ctx, fullKey, jsonValue, ttl).Err()
}

func (r *RedisCache) Get(ctx context.Context, key string) (interface{}, error) {
	fullKey := r.keyPrefix + key
	val, err := r.client.Get(ctx, fullKey).Bytes()
	if err == redis.Nil {
		r.stats.Misses++
		return nil, nil
	} else if err != nil {
		return nil, err
	}

	var result interface{}
	if err := json.Unmarshal(val, &result); err != nil {
		return nil, err
	}

	r.stats.Hits++
	return result, nil
}

func (r *RedisCache) Delete(ctx context.Context, key string) error {
	fullKey := r.keyPrefix + key
	return r.client.Del(ctx, fullKey).Err()
}

func (r *RedisCache) Exists(ctx context.Context, key string) (bool, error) {
	fullKey := r.keyPrefix + key
	result, err := r.client.Exists(ctx, fullKey).Result()
	return result == 1, err
}

func (r *RedisCache) SetWithPrimaryKey(ctx context.Context, primaryKey string, value interface{}, ttl time.Duration) error {
	return r.Set(ctx, primaryKey, value, ttl)
}

func (r *RedisCache) GetByPrimaryKey(ctx context.Context, primaryKey string) (interface{}, error) {
	return r.Get(ctx, primaryKey)
}

func (r *RedisCache) AddIndex(ctx context.Context, indexName string, indexKey string, primaryKey string) error {
	indexRedisKey := fmt.Sprintf("%sindex:%s:%s", r.keyPrefix, indexName, indexKey)
	return r.client.SAdd(ctx, indexRedisKey, primaryKey).Err()
}

func (r *RedisCache) GetByIndex(ctx context.Context, indexName string, indexKey string) ([]interface{}, error) {
	indexRedisKey := fmt.Sprintf("%sindex:%s:%s", r.keyPrefix, indexName, indexKey)
	primaryKeys, err := r.client.SMembers(ctx, indexRedisKey).Result()
	if err != nil {
		return nil, err
	}

	var results []interface{}
	for _, pk := range primaryKeys {
		val, err := r.Get(ctx, pk)
		if err != nil {
			return nil, err
		}
		if val != nil {
			results = append(results, val)
		}
	}

	return results, nil
}

func (r *RedisCache) RemoveIndex(ctx context.Context, indexName string, indexKey string, primaryKey string) error {
	indexRedisKey := fmt.Sprintf("%sindex:%s:%s", r.keyPrefix, indexName, indexKey)
	return r.client.SRem(ctx, indexRedisKey, primaryKey).Err()
}

func (r *RedisCache) CleanExpired(ctx context.Context) error {
	// Redis会自动处理过期键
	return nil
}

func (r *RedisCache) Stats() iface.CacheStats {
	// Redis统计信息需要额外实现
	return r.stats
}
